<?php namespace lfy;

\lfy\setconst('LFY_LEAF_TEMPLATE', '\lfy\template\html');
\lfy\setconst('LFY_TEMPLATE_MODE', 'chain');
\lfy\setconst('LFY_CACHE_OUTPUT', false);

//! this class itself is no good as a template, but its ancestors are

abstract class template extends schematic{

	//! headers we intend to send, to be read and sent by pot

	public static $headers = array(
		'Content-type' => 'text/html; charset=utf-8'
	);

	//! merges static member array from base class right up to called_class.
	/*! 
		allows descendant templates to merge their static arrays with that of their parent class(es)
		\param k name of static member array
		@return array merged array
	*/

	public static function get_merged_static($k){
		$c = get_called_class();
		$s = $c::schema();
		if(!$a = $s->get($k)){
			$a = isset($c::$$k) ? $c::$$k : array();
			$p = get_parent_class($c);
			if($p != get_class()) $a = array_merge($p::get_merged_static($k), $a);
			$s->set($k, $a);
		}
		return $a;			
	}

	public static function leaf_merge($k, $leaf){
		$a = ($b = $leaf->property("template_$k")) ? (array)$b : array();
		//$called_class = get_called_class();
		//var_dump($k, $called_class, $called_class::get_merged_static($k));
		return array_merge(self::get_merged_static($k), $a);
	}

	//! runs template family tree in manner according mode.
	/*! 
		included modes are:
		- 'unit': topmost (youngest descendant) template only
		- 'stack': topmost template first, passing its output to each ancestor in turn
		- 'chain': bottommost (oldest ancestor) template first, calling each descendant in turn

		returns output if cache passed as true, null otherwise
	
		\param leaf \\lfy\\leaf leaf
		\param cache bool whether we intend to cache the output
		\param mode order in which to run templates, defaulting to env LFY_TEMPLATE_MODE
		@return mixed
	*/

	public static function run($leaf, $mode = null, $cache = null){

		if(is_null($cache)) $cache = getenv('LFY_CACHE_OUTPUT');

		if(!$mode) $mode = getenv('LFY_TEMPLATE_MODE');	

		$top_class = get_called_class();
	
		switch($mode){

			case 'unit':
				if($cache) ob_start();
				$top_class::open($leaf);
				echo $top_class::process($leaf);
				$top_class::close($leaf);
				return ($cache) ? ob_get_clean() : null;
	
			case 'stack':
				ob_start();
				echo $top_class::process($leaf);
				$content = ob_get_clean();
				return self::run_stack($leaf, $top_class, $content);
			
			case 'chain':
				if($cache) ob_start();
				self::open_chain($leaf, $top_class);
				echo $top_class::process($leaf);
				self::close_chain($leaf, $top_class);
				return ($cache) ? ob_get_clean() : null;
			
			default:
				$try = "run_$mode";
				if(method_exists($top_class, $try)){
					if($cache) ob_start();
					self::$try($leaf, $top_class);
					return ($cache) ? ob_get_clean() : null;
				}
			
				back_error("No template method '$try' for mode '$mode' in class $top_class", E_USER_ERROR);	
			
		}
	}

	//! process hook. 
	/*! Gets output of leaf by calling $leaf->process() before templates run, may be rewritten by descendants
		\param leaf to get output from
		@return string
	*/

	public static function process($leaf){
		return $leaf->process();
	}

	//! stack mode method.
	/*!
		Runs youngest descendant template first, passing its output to each ancestor in turn.
		\param leaf
		\param class next class to call
		\param content string to add to
		@return string
	*/

	private static function run_stack($leaf, $class, $content=false){

		ob_start();

		$class::open($leaf);

		echo $content;
	
		$class::close($leaf);
	
		$content = ob_get_clean();
	
		// find and run parent
		$class = get_parent_class($class);

		// If we're not back to \lfy\template, there's a parent
		if($class != get_class()) $content = $class::run_stack($leaf, $class, $content);
	
		return $content;
	}

	//! chain mode open method
	/*!
		Runs oldest ancestor first, calling each descendant's open function in turn
		\param leaf
		\param class next class to call
		@return string
	*/

	private static function open_chain($leaf, $class){
		$parent = get_parent_class($class);
		if($parent != get_class()) self::open_chain($leaf, $parent);
		$class::open($leaf);
	}

	//! chain mode close method
	/*!
		Runs after open_chain() has run its course, in opposite order
		\param leaf
		\param class next class to call
		@return string
	*/

	private static function close_chain($leaf, $class){
		$class::close($leaf);
		$parent = get_parent_class($class);
		if($parent != get_class()) self::close_chain($leaf, $parent);
	}

	//! open method to be rewritten by descendants.

	abstract protected static function open($leaf);

	//! close method to be rewritten by descendants.

	abstract protected static function close($leaf);

}

